local ngx = ngx
local require  = require
local log      = require("modules.commhm.log")
local utils    = require("modules.business_common.business_utils")
local config   = require("modules.business_common.business_config")
local http     = require("modules.business_common.utils.business_http")
local mall     = require("modules.business_common.bridge.business_mall")
local profile  = require("modules.business_common.bridge.business_profile")
local business = require("modules.business_common.bridge.business_business")
local gmcost   = require("modules.business_common.bridge.business_gmcost")
local asset_mgr = require("modules.business_common.bridge.business_assetmgr")

local ngx = ngx
local type = type
local curr = os.time
local table = table
local pairs = pairs
local ipairs = ipairs
local tostring = tostring
local tonumber = tonumber
local table_sort = table.sort
local loadstring = table.loadstring
local to_str = utils.hm.to_str
local json2table = utils.hm.json2table
local table2json = utils.hm.table2json
local tableNotEmpty = utils.hm.tableNotEmpty



local Capital = {
    _VERSION = "0.0.1",
    Type = {
        Weponskin  = 1,     -- 武器装扮
        Spraypaint = 2,     -- 喷漆
        Roleskin   = 3,     -- 装扮
        Roleitem   = 4,     -- 道具
        Rolemount  = 5,     -- 坐骑
        Headframe  = 6,     -- 头像框
        Avatar     = 7,     -- 装扮部件
    }
}

-- 数据校验调试
local CapitalRelease = false
-- 查询代理开关配置
local QuerySwitch = {
    [Capital.Type.Roleskin]   = false,
    [Capital.Type.Roleitem]   = false,
    [Capital.Type.Rolemount]  = false,
    [Capital.Type.Avatar]     = false,
}

local capital_router = {
    [Capital.Type.Roleskin]   = "/roleskin/v1",
    [Capital.Type.Roleitem]   = "/roleitem/v1",
    [Capital.Type.Rolemount]  = "/mount/v1",
    [Capital.Type.Avatar]     = "/avatar/v1",
}

local sorted_wrap = function (request_func)
    return CapitalRelease and request_func or function (uin)
        local succ, list = request_func(uin)
        if succ then table_sort(list) end
        return succ, list
    end
end

-- return : bool, table
local query_from_repos = {
    [Capital.Type.Roleitem]   = gmcost.items_query_all,
    [Capital.Type.Weponskin]  = mall.query_all_weponskins,
    [Capital.Type.Spraypaint] = business.query_all_spraypaints,
    [Capital.Type.Headframe]  = profile.query_owned_box_account,
    [Capital.Type.Roleskin]   = sorted_wrap(gmcost.query_owned_skin_account),
    [Capital.Type.Rolemount]  = sorted_wrap(gmcost.query_owned_mount_account),
    [Capital.Type.Avatar]     = sorted_wrap(gmcost.query_owned_avatar_account),
}

local capital_switch = function (capitalType)
    if not config.capital_server_enable then
        return false
    end
    return QuerySwitch[capitalType]
end

local http_get = function ( router, uin, request )
    request = request or {}
    request.uin  = uin
    request.time = curr()
    request.auth = ngx.md5 ( request.time .. '#miniw_907#'  .. (tostring(uin) or '') )

    local server = config.get_capital_server_url(uin) .. router
    local response, code = http.Get(server, request)
    if response then
        return true, json2table(response)
    end

    http.Log(to_str({server = server, request = request, response = response, code = code}))
    return false
end

local http_post = function (router, uin, data, request)
    request = request or {}
    request.uin  = uin
    request.time = curr()
    request.auth = ngx.md5 ( request.time .. '#miniw_907#'  .. (tostring(uin) or '') )

    local header = {
        ["Content-Type"] = "application/json",
        ["Connection"]   = "Keep-Alive",
        ["User-Agent"]   = "mnw",
    }

    local server = config.get_capital_server_url(uin) .. router
    local body = tableNotEmpty(data or {}) and table2json(data) or ""
    local response, code = http.Post(server, request, body, header)
    if response then
        return true, json2table(response)
    end

    return false, code
end

local get_capital = function (router, uin, request)
    return http_get(router .. "/get", uin, request)
end

local del_capital = function (router, uin, request)
    return http_get(router .. "/del", uin, request)
end

local check_capital = function (router, uin, data, request)
    return http_post(router .. "/check", uin, data, request)
end

local request_capital_weponskin = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    return get_capital(capital_router[Capital.Type.Weponskin], uin)
end

local request_capital_spraypaint = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    return get_capital(capital_router[Capital.Type.Spraypaint], uin)
end

local request_capital_roleskins = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    local succ, data = get_capital(capital_router[Capital.Type.Roleskin], uin)
    if not succ then
        return false
    end

    return true, utils.table2list(data, function (_, roleskin)
        if type(roleskin) == "table" and tonumber(roleskin.ExpireTime) == -1 then --永久
            return roleskin.SkinID
        end
    end)
end

local request_capital_roleitems = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    return get_capital(capital_router[Capital.Type.Roleitem], uin)
end

local request_capital_mounts = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    local succ, data = get_capital(capital_router[Capital.Type.Rolemount], uin)
    if not succ then
        return false
    end
    local mount_list = {}
    for _, info in pairs(data or {}) do
        for i=0, info.RiderLevel or 0 do
            mount_list[#mount_list + 1] = info.RiderID + i
        end
    end
    return true, mount_list
end

local request_capital_headframs = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    local succ, data = get_capital(capital_router[Capital.Type.Headframe], uin)
    if not succ then
        return false
    end
    return true, utils.table2list(data)
end

local request_capital_avatar = function (uin)
    uin = uin or ngx.ctx.m_params.uin
    local succ, data = get_capital(capital_router[Capital.Type.Avatar], uin)
    if not succ then
        return false
    end
    return true, utils.table2list(data, function (_, avatar)
        if avatar.ExpireTime == -1 then
            return avatar.ModelID
        end
    end)
end

local query_from_capital = {
    [Capital.Type.Roleitem]   = request_capital_roleitems,
    [Capital.Type.Rolemount]  = sorted_wrap(request_capital_mounts),
    [Capital.Type.Avatar]     = sorted_wrap(request_capital_avatar),
    [Capital.Type.Roleskin]   = sorted_wrap(request_capital_roleskins),
}

local send_to_check = function (capitalType, uin, data)
    if not (config.capital_server_enable and capital_router[capitalType]) then
        return false
    end
    return check_capital(capital_router[capitalType], uin, data)
end

local renew_capital = function (capitalType, uin, request)
    uin = uin or ngx.ctx.m_params.uin
    if not (config.capital_server_enable and capital_router[capitalType]) then
        return false
    end
    return del_capital(capital_router[capitalType], uin, request)
end

-- return : bool, table
local query_capital = function (capitalType, uin)
    uin = uin or ngx.ctx.m_params.uin

    -- 1. 是否配置并打开了资产查询服的接口
    local succ, capital_data, source_data
    if query_from_capital[capitalType] and capital_switch(capitalType) then
        -- 2. 尝试获取资产查询服的数据
        succ, capital_data = query_from_capital[capitalType](uin)
        if not succ then
            log.day_list("capital_error", uin, capital_router[capitalType] or capitalType, to_str({
                result = succ,
                data   = capital_data
            }))

        -- 3. 非测试状态直接返回
        elseif CapitalRelease then return true, capital_data end
    end

    if not query_from_repos[capitalType] then
        log.day_list("capital_error", uin, capital_router[capitalType] or capitalType, "unkown capital repos")
        return false
    end

    -- 4. 去数据源拉最新的数据
    succ, source_data = query_from_repos[capitalType](uin)
    if succ and not CapitalRelease and config.capital_server_enable then

        --send_to_check(capitalType, uin, source_data)

        if config.debug_env() and capital_data and source_data then
            local capital_str = to_str(capital_data or {})
            local source_str = to_str(source_data or {})
            if capital_str ~= source_str then
                log.day_list("capital_error", uin, capital_router[capitalType] or capitalType, to_str({
                    repos = source_str,
                    cache = capital_str,
                }))    -- body
            end
        end
    end

    return succ, source_data
end

local _query_roleitem_num = function (uin, itemid)
    local succ, data = Capital.query_roleitems(uin)
    if not succ then
        return false
    end
    for _, item in ipairs(data or {}) do
        if item.ItemID == itemid then
            return true, item.Num
        end
    end
    return true, 0
end

local _query_owned_spraypaints = function (uin)
    local succ, data = Capital.spraypaint_data(uin)
    if not succ then
        return succ, data
    end

    return true, utils.table2list(data, function (_, spraypaint)
        return spraypaint.ownedTime == -1 and spraypaint.id or nil
    end)
end

local _query_owned_weponskin = function (uin)
    local succ, data = Capital.weponskin_data(uin)
    if not succ then
        return succ, data
    end

    return true, utils.table2list(data, function (_, weponskin)
        return weponskin.expired == -1 and weponskin.id or nil
    end)
end

local _add_roleitems = function (uin, items, reason, ext, approach)
    if not config.capital_mgr_enabled then
        return gmcost.items_add(uin, items, reason, ext)
    else
        return asset_mgr.item_add(uin, items, approach or 'add_approach_old', reason, ext)
    end
end

local _del_roleitem_one = function (uin, itemid, num, reason, ext, approach)
    if not config.capital_mgr_enabled then
        return gmcost.item_remove(uin, itemid, num, reason, ext)
    else
        return gmcost.item_remove(uin, itemid, num, reason, ext)
        --return asset_mgr.item_remove(uin, itemid, num, approach or 'del_approach_old', reason, ext)
    end
end

local _del_roleitem_multi  = function (uin, items, reason, ext, approach)
    if not config.capital_mgr_enabled then
        return gmcost.items_remove(uin, items, reason, ext)
    else
        return gmcost.items_remove(uin, items, reason, ext)
        --return asset_mgr.items_remove(uin, items, approach or 'del_approach_old', reason, ext)
    end
end

local _add_headframe = function (frame_id, dst_uin, src_uin)
    return profile.open_head_frame(frame_id, dst_uin, src_uin)
end

local _add_headframes = function (frame_lst, dst_uin, src_uin)
    return profile.open_vip_head_frame(frame_lst, dst_uin, src_uin)
end

local _del_headframes = function (frame_lst, dst_uin, src_uin)
    return profile.lock_vip_head_frame(frame_lst, dst_uin, src_uin)
end

local _add_roleskin = function (skinid, reason, uin, approach)
    if not config.capital_mgr_enabled then
        return gmcost.add_skin(skinid, reason, uin)
    else
        return gmcost.add_skin(skinid, reason, uin)
        --return asset_mgr.add_skin(skinid, reason, uin, approach or 'add_approach_old')
    end
end

local _add_roleskin_list = function (skinlist, reason, uin, approach)
    if not config.capital_mgr_enabled then
        return gmcost.add_skin_list(skinlist, reason, uin)
    else
        return gmcost.add_skin_list(skinlist, reason, uin)
        --return asset_mgr.add_skin(skinid, reason, uin, approach or 'add_approach_old')
    end
end

local _del_roleskin = function (skinid, reason, uin, approach)
    if not config.capital_mgr_enabled then
        return gmcost.remove_skin(skinid, reason, uin)
    else
        return gmcost.remove_skin(skinid, reason, uin)
        --return asset_mgr.remove_skin(skinid, reason, uin, approach or 'del_approach_old')
    end
end

local _use_roleskin = function (skinid, uin)
    return gmcost.use_skin(skinid, uin)
end

local _modify_skins_expiretime = function( uin, skinlst, reason)
    return gmcost.modify_skins_expiretime(uin, skinlst, reason)
end

local _add_avatar = function (modelid, reason, dst_uin, src_uin, approach)
    if not config.capital_mgr_enabled then
        return gmcost.add_avatar(modelid, reason, dst_uin, src_uin)
    else
        return gmcost.add_avatar(modelid, reason, dst_uin, src_uin)
        --return asset_mgr.add_avatar(modelid, reason, dst_uin, src_uin, approach or 'add_approach_old')
    end
end

local _add_rolemount = function (mountid, level, reason, uin, approach)
    if not config.capital_mgr_enabled then
        return gmcost.add_mount(mountid, level, reason, uin)
    else
        return gmcost.add_mount(mountid, level, reason, uin)
        --return asset_mgr.add_mount(mountid, level, reason, uin, approach or 'add_approach_old')
    end
end

-- 道具
Capital.add_roleitems    = _add_roleitems
Capital.del_roleitem     = _del_roleitem_one
Capital.del_roleitems    = _del_roleitem_multi
Capital.query_roleitems  = function (uin) return query_capital(Capital.Type.Roleitem, uin) end
Capital.query_roleitem_num = _query_roleitem_num
-- 头像框
Capital.add_headframe    = _add_headframe
Capital.add_headframes   = _add_headframes
Capital.del_headframes   = _del_headframes
Capital.query_headframe  = function (uin) return query_capital(Capital.Type.Headframe, uin) end
-- 武器装扮
Capital.weponskin_data   = function (uin) return query_capital(Capital.Type.Weponskin, uin) end
Capital.query_weponskin  = _query_owned_weponskin
-- 喷漆
Capital.spraypaint_data  = function (uin) return query_capital(Capital.Type.Spraypaint, uin) end
Capital.query_spraypaint = _query_owned_spraypaints
-- 装扮套装
Capital.add_roleskin      = _add_roleskin
Capital.add_roleskins     = _add_roleskin_list
Capital.del_roleskin      = _del_roleskin
Capital.use_roleskin    = _use_roleskin
Capital.modify_skins_expiretime = _modify_skins_expiretime
Capital.query_roleskins   = function (uin) return query_capital(Capital.Type.Roleskin, uin) end
-- 装扮部件
Capital.add_avatar       = _add_avatar
Capital.query_avatars    = function (uin) return query_capital(Capital.Type.Avatar, uin) end
-- 坐骑
Capital.add_rolemount    = _add_rolemount
Capital.query_rolemount  = function (uin) return query_capital(Capital.Type.Rolemount, uin) end

return Capital